﻿using SSLCertHelper.Common;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Path = System.IO.Path;

namespace SSLCertHelper
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        AppConfig config = new AppConfig();
        public MainWindow()
        {
            InitializeComponent();
        }

        private void btnCreateRootCert_Click(object sender, RoutedEventArgs e)
        {
            string issue = txtRootIssue.Text.Trim();
            string pwd = txtRootPwd.Text.Trim();
            string subject = issue;
            string savePath = Path.Combine(AppConfig.RootPath, txtRootSavePath.Text.Trim());
            if(CreateNewRootCert(config, subject, pwd, savePath))
            {
                MessageBox.Show("创建根证书成功");
            }
            else
            {
                MessageBox.Show("创建根证书失败");
            }
        }


        private static bool CreateNewRootCert(AppConfig config, string subject, string pwd, string savePath)
        {
            LogHelper.WriteLog("创建证书");
            try
            {
                string rootCertName = "rootcert.pem";
                string rootKeyName = "rootkey.pem";
                if (!Directory.Exists(savePath))
                    Directory.CreateDirectory(savePath);
                string opensslPath = Path.GetFullPath(config.OpensslPath);
                string opensslDir = Path.GetDirectoryName(opensslPath);
                string tmpCertName = "tempcert.pem";
                string tmpKeyName = "tempkey.pem";

                string cnfFile = Path.Combine(opensslDir, "openssl.cnf");
                string cmd1 = "req -config \"" + cnfFile + "\" -new -nodes -keyout "+ tmpKeyName + " -out sslreq.pem -newkey rsa:2048 -subj \"" + subject + "\"";
                string cmd2 = "ca -config  \"" + cnfFile + "\" -utf8 -out sslcert.pem -md sha256 -extensions ssl -batch -infiles sslreq.pem";
                string cmd3 = "x509 -in sslcert.pem -out "+ tmpCertName;

                ProcessChild(opensslPath, cmd1, opensslDir);
                ProcessChild(opensslPath, cmd2, opensslDir);
                ProcessChild(opensslPath, cmd3, opensslDir);


                if (File.Exists(Path.Combine(opensslDir, "sslcert.pem")))
                    File.Delete(Path.Combine(opensslDir, "sslcert.pem"));
                if (File.Exists(Path.Combine(opensslDir, "sslreq.pem")))
                    File.Delete(Path.Combine(opensslDir, "sslreq.pem"));

                string srcSerFile = Path.Combine(savePath,rootCertName);
                string srcSerKeyFile = Path.Combine(savePath, rootKeyName);
                if (File.Exists(srcSerFile + "_old"))
                    File.Delete(srcSerFile + "_old");
                if (File.Exists(srcSerKeyFile + "_old"))
                    File.Delete(srcSerKeyFile + "_old");
                if (File.Exists(srcSerFile))
                    File.Move(srcSerFile, srcSerFile + "_old");
                if (File.Exists(srcSerKeyFile))
                    File.Move(srcSerKeyFile, srcSerKeyFile + "_old");
                File.Move(Path.Combine(opensslDir, tmpCertName), srcSerFile);
                File.Move(Path.Combine(opensslDir, tmpKeyName), srcSerKeyFile);

                if (CheckCertTime(srcSerFile))
                {
                    LogHelper.WriteLog("创建证书成功");
                }
                else
                    LogHelper.WriteLog("创建证书失败");
            }
            catch (Exception ex)
            {
                LogHelper.WriteLog("创建证书失败", ex);
            }

            return false;
        }
        private static bool CreateNewCert(AppConfig config)
        {
            LogHelper.WriteLog("创建证书");
            try
            {
                string opensslPath = Path.GetFullPath(config.OpensslPath);
                string opensslDir = Path.GetDirectoryName(opensslPath);
                if (File.Exists(Path.Combine(opensslDir, "server.pem")))
                    File.Delete(Path.Combine(opensslDir, "server.pem"));
                if (File.Exists(Path.Combine(opensslDir, "serkey.pem")))
                    File.Delete(Path.Combine(opensslDir, "serkey.pem"));
                string ip = GetLocalIPv4();
                string subject = config.CertSubject.Replace("{ip}", ip);
                string cnfFile = Path.Combine(opensslDir, "openssl.cnf");
                string cmd1 = "req -config \"" + cnfFile + "\" -new -nodes -keyout serkey.pem -out sslreq.pem -newkey rsa:2048 -subj \"" + subject + "\"";
                string cmd2 = "ca -config  \"" + cnfFile + "\" -utf8 -out sslcert.pem -md sha1 -extensions ssl -batch -infiles sslreq.pem";
                string cmd3 = "x509 -in sslcert.pem -out server.pem";

                ProcessChild(opensslPath, cmd1, opensslDir);
                ProcessChild(opensslPath, cmd2, opensslDir);
                ProcessChild(opensslPath, cmd3, opensslDir);


                if (File.Exists(Path.Combine(opensslDir, "sslcert.pem")))
                    File.Delete(Path.Combine(opensslDir, "sslcert.pem"));
                if (File.Exists(Path.Combine(opensslDir, "sslreq.pem")))
                    File.Delete(Path.Combine(opensslDir, "sslreq.pem"));

                string srcSerFile = Path.Combine(config.CertPath, config.ServerCertName);
                string srcSerKeyFile = Path.Combine(config.CertPath, config.ServerKeyName);
                if (File.Exists(srcSerFile + "_old"))
                    File.Delete(srcSerFile + "_old");
                if (File.Exists(srcSerKeyFile + "_old"))
                    File.Delete(srcSerKeyFile + "_old");
                if (File.Exists(srcSerFile))
                    File.Move(srcSerFile, srcSerFile + "_old");
                if (File.Exists(srcSerKeyFile))
                    File.Move(srcSerKeyFile, srcSerKeyFile + "_old");
                File.Move(Path.Combine(opensslDir, "server.pem"), srcSerFile);
                File.Move(Path.Combine(opensslDir, "serkey.pem"), srcSerKeyFile);

                if (CheckCertTime(srcSerFile))
                {
                    LogHelper.WriteLog("更新证书成功");
                }
                else
                    LogHelper.WriteLog("更新证书失败");
            }
            catch (Exception ex)
            {
                LogHelper.WriteLog("创建证书失败", ex);
            }

            return false;
        }
        private static void ProcessChild(string exe, string cmd, string workdir)
        {
            try
            {
                Console.WriteLine(exe + " " + cmd);
                Process process1 = new Process();
                process1.StartInfo = new ProcessStartInfo(exe, cmd);
                process1.StartInfo.WorkingDirectory = workdir;
                process1.OutputDataReceived += Process1_OutputDataReceived;
                process1.Start();
                process1.WaitForExit();
            }
            catch (Exception ex)
            {
                LogHelper.WriteLog("执行命令失败：" + cmd, ex);
            }
        }

        private static void Process1_OutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            LogHelper.WriteLog("Child: " + e.Data);
        }
        /// <summary>
        /// 检查证书时间
        /// </summary>
        /// <param name="certFile"></param>
        /// <returns></returns>
        private static Boolean CheckCertTime(string certFile)
        {
            if (File.Exists(certFile))
            {
                try
                {
                    StringBuilder builder = new StringBuilder();

                    var lines = File.ReadAllLines(certFile, Encoding.UTF8);
                    foreach (var item in lines)
                    {
                        if (item.StartsWith("--") && item.Contains("BEGIN CERTIFICATE"))
                        {
                            builder.Clear();
                            continue;
                        }
                        if (item.StartsWith("--") && item.Contains("END CERTIFICATE"))
                        {
                            break;
                        }
                        builder.Append(item.Trim());
                    }


                    System.Security.Cryptography.X509Certificates.X509Certificate2 cert = new System.Security.Cryptography.X509Certificates.X509Certificate2(Convert.FromBase64String(builder.ToString()));

                    Console.WriteLine("Subject={0}", cert.Subject);
                    Console.WriteLine("NotAfter={0}", cert.NotAfter.ToString());
                    Console.WriteLine("NotBefore={0}", cert.NotBefore.ToString());
                    if (cert.NotAfter > DateTime.Now.AddDays(7))
                        return true;
                }
                catch (Exception ex)
                {
                    LogHelper.WriteLog("解析证书失败", ex);
                }
            }

            return false;
        }
        /// <summary>
        /// 获取本机IP
        /// </summary>
        /// <returns></returns>
        private static string GetLocalIPv4()
        {
            try
            {
                var iplist = Dns.GetHostAddresses(Dns.GetHostName());

                for (int i = 0; i < iplist.Length; i++)
                {
                    if (iplist[i].IsIPv6Teredo)
                        continue;
                    if (iplist[i].IsIPv6LinkLocal || iplist[i].IsIPv6SiteLocal || iplist[i].IsIPv6Multicast)
                        continue;
                    string ip = iplist[i].ToString();
                    Console.WriteLine("ip: {0}", ip);
                    return ip;
                }


            }
            catch (Exception ex)
            {
                LogHelper.WriteLog("获取本机IP失败", ex);
            }


            return "127.0.0.1";
        }

        private void btnCreateCACert_Click(object sender, RoutedEventArgs e)
        {

        }

        private void btnCreateUserCert_Click(object sender, RoutedEventArgs e)
        {

        }
    }
}
